#include <iostream>
#include <vector>
#include <string>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <GL/gl.h>

#include "object3d.h"
#include "vertex.h"

static const int MAX_LODS_IN_FILE = 32;
static const int MAX_VERTICES_IN_LOD = (int)1e5;
static const int MAX_TRIANGLES_IN_LOD = (int)1e5;
static const int MAX_MATERIALS_IN_LOD = 100;

using std::cout;
using std::endl;

//todo delete this after testing
static std::vector< object3dv<vertex> > L;

int save_lods(const std::string& file, const std::vector< object3dv<vertex> >& lods)
{
   FILE* fs;
   fs = fopen(file.c_str(), "w");
   if(fs!=NULL)
   {
      //todo we're overwriting the file
      
      int lodscount;
      unsigned uend;
      size_t size;
      
      
      lodscount=0;
      uend=lods.size();
      for(unsigned i=0; i<uend; i++) {
         if(lods[i].vcount>0)
            lodscount++;
      }
      
      if(lodscount<=0) {
         //todo
         assert(1==2);
      }
      
      // a count of lods
      size = fwrite(&lodscount,sizeof(unsigned),1,fs);
      if(size!=1) {
         //todo
         assert(1==0);
      }

      // for each LOD...
      for(unsigned ii=0; ii<uend; ii++)
      {
         if(lods[ii].vcount>0)
         {
            int end;

            // vertices' count
            size = fwrite(&lods[ii].vcount,sizeof(lods[ii].vcount),1,fs);
            if(size!=1) {
               //todo
               assert(1==0);
            }
            
            end = lods[ii].vcount;
            assert((unsigned)lods[ii].vcount==lods[ii].vects.size());
            const std::vector<vertex>& vs = lods[ii].vects;
            for(int i=0; i<end; i++)
            {
               // vertex coordinate triple
               size = fwrite(vs[i].xyz, sizeof(float), 3, fs);
               if(size!=3) {
                  //todo
                  assert(1==0);
               }
               // vertex normal vector
               size = fwrite(vs[i].normal, sizeof(float), 3, fs);
               if(size!=3) {
                  //todo
                  assert(1==0);
               }
            }

            // a count of triangles
            size = fwrite(&lods[ii].tcount, sizeof(lods[ii].tcount), 1, fs);
            if(size!=1) {
               //todo
               assert(1==3);
            }

            end = lods[ii].tcount;
            assert((unsigned)lods[ii].tcount == lods[ii].tris.size());
            const std::vector<triangle>& ts = lods[ii].tris;
            for(int i=0; i<end; i++)
            {
               // triangles (each one = 3 indices)
               size = fwrite(ts[i].verts, sizeof(int), 3, fs);
               if(size!=3) {
                  //todo
                  assert(1==2);
               }
            }

            // a count of materials
            size = fwrite(&lods[ii].mcount, sizeof(lods[ii].mcount), 1, fs);
            if(size!=1) {
               //todo
               assert(3==2);
            }

            end = lods[ii].mcount;
            assert((unsigned)lods[ii].mcount == lods[ii].mats.size());
            for(int i=0; i<end; i++)
            {
               // diffuse component
               size = fwrite(lods[ii].mats[i].diffuse, sizeof(GLfloat), 3, fs);
               if(size!=3) {
                  //todo
                  assert(2==1);
               }

               // specular component
               size = fwrite(lods[ii].mats[i].specular, sizeof(GLfloat), 3, fs);
               if(size!=3) {
                  //todo
                  assert(2==1);
               }
            }
         }
      }

      L=lods;
      fclose(fs);
      return 0;
   }
   else
   {
      //todo
      return 1;
   }
}



// After a call to this function structure `lods' is changed
// even if the function fail.
int load_lods(const std::string& file, std::vector< object3dv<vertex> >& lods)
{
   FILE* fs;
   fs = fopen(file.c_str(), "r");
   if(fs!=NULL)
   {
      unsigned lodscount;
      size_t size;

      size = fread(&lodscount, sizeof(unsigned), 1, fs);
      if(size!=1) {
         //todo
         assert(1==0);
      }
      

      // lodscount is unsigned => lodscount >= 0
      if(lodscount>(unsigned)MAX_LODS_IN_FILE) {
         //todo
         assert(2==3);
      }

      // prepare the space 
      lods.resize(lodscount);
      for(unsigned i=0; i<lodscount; i++)
         lods[i].clear();

      for(unsigned ii=0; ii<lodscount; ii++)
      {
         int end;

   // vertices
         size = fread(&lods[ii].vcount, sizeof(lods[ii].vcount), 1, fs);
         if(size!=1) {
            //todo
            assert(1==0);
         }

         if(lods[ii].vcount<0 || lods[ii].vcount>MAX_VERTICES_IN_LOD) {
            //todo
            assert(1==0);
         }

         lods[ii].vects.resize(lods[ii].vcount);

         end = lods[ii].vcount;
         std::vector<vertex>& vs = lods[ii].vects;
         for(int i=0; i<end; i++)
         {
            size = fread(vs[i].xyz, sizeof(float), 3, fs);
            if(size!=3) {
               //todo
               assert(3==4);
            }

            // normal
            size = fread(vs[i].normal, sizeof(float), 3, fs);
            if(size!=3) {
               //todo
               assert(3==4);
            }

            if(vs[i].normal[0]==0.0f && vs[i].normal[1]==0.0f && vs[i].normal[2]==0.0f) {
               //todo
               assert(1==2);
            }

            // normalize vector
            // todo after tests, uncomment the following:
            //vs[i].normalize_normalv();
         }


   // triangles
         size = fread(&lods[ii].tcount, sizeof(lods[ii].tcount), 1, fs);
         if(size!=1) {
            //todo
            assert(1==0);
         }

         if(lods[ii].tcount<0 || lods[ii].tcount>MAX_TRIANGLES_IN_LOD) {
            //todo
            assert(1==2);
         }

         lods[ii].tris.resize(lods[ii].tcount);

         end = lods[ii].tcount;
         std::vector<triangle>& ts = lods[ii].tris;
         for(int i=0; i<end; i++)
         {
            size = fread(ts[i].verts, sizeof(int), 3, fs);
            if(size!=3) {
               //todo
               assert(1==2);
            }

            if(ts[i].verts[0]<0 || ts[i].verts[0]>=end ||
               ts[i].verts[1]<0 || ts[i].verts[1]>=end ||
               ts[i].verts[2]<0 || ts[i].verts[2]>=end) {
               //todo at least one index building this triangle is incorrect
               assert(1==2);
            }
         }

   // materials
         size = fread(&lods[ii].mcount, sizeof(lods[ii].mcount), 1, fs);
         if(size!=1) {
            //todo
            assert(2==0);
         }

         if(lods[ii].mcount<0 || lods[ii].mcount>MAX_MATERIALS_IN_LOD) {
            //todo
            assert(2==3);
         }

         lods[ii].mats.resize(lods[ii].mcount);

         end = lods[ii].mcount;
         for(int i=0; i<end; i++)
         {
            size = fread(lods[ii].mats[i].diffuse, sizeof(GLfloat), 3, fs);
            if(size!=3) {
               //todo
               assert(2==3);
            }

            if(lods[ii].mats[i].diffuse[0]<0.0f || lods[ii].mats[i].diffuse[0]>1.0f ||
               lods[ii].mats[i].diffuse[1]<0.0f || lods[ii].mats[i].diffuse[1]>1.0f ||
               lods[ii].mats[i].diffuse[2]<0.0f || lods[ii].mats[i].diffuse[2]>1.0f) {
               //todo
               assert(3==5);
            }

            size = fread(lods[ii].mats[i].specular, sizeof(GLfloat), 3, fs);
            if(size!=3) {
               //todo
               assert(2==3);
            }

            if(lods[ii].mats[i].specular[0]<0.0f || lods[ii].mats[i].specular[0]>1.0f ||
               lods[ii].mats[i].specular[1]<0.0f || lods[ii].mats[i].specular[1]>1.0f ||
               lods[ii].mats[i].specular[2]<0.0f || lods[ii].mats[i].specular[2]>1.0f) {
               //todo
               assert(3==2);
            }
         }
      }

      /*for(unsigned ii=0; ii<lods.size(); ii++)
      {
         cout<<lods[ii].vcount<<endl;
         for(int i=0; i<lods[ii].vcount; i++) {
            cout<<lods[ii].vects[i].xyz[0]<<" ";
            cout<<lods[ii].vects[i].xyz[1]<<" ";
            cout<<lods[ii].vects[i].xyz[2]<<endl;
         }
         for(int i=0; i<lods[ii].vcount; i++) {
            cout<<lods[ii].vects[i].normal[0]<<" ";
            cout<<lods[ii].vects[i].normal[1]<<" ";
            cout<<lods[ii].vects[i].normal[2]<<endl;
         }
         for(int i=0; i<lods[ii].tcount; i++) {
            cout<<lods[ii].tris[i].verts[0]<<"  ";
            cout<<lods[ii].tris[i].verts[1]<<"  ";
            cout<<lods[ii].tris[i].verts[2]<<endl;
         }
         for(int i=0; i<lods[ii].mcount; i++) {
            cout<<lods[ii].mats[i].diffuse[0]<<"\t";
            cout<<lods[ii].mats[i].diffuse[1]<<"\t";
            cout<<lods[ii].mats[i].diffuse[2]<<endl;
            cout<<lods[ii].mats[i].specular[0]<<"\t";
            cout<<lods[ii].mats[i].specular[1]<<"\t";
            cout<<lods[ii].mats[i].specular[2]<<endl;
         }
         cout<<lods[ii].tcount<<endl;
         cout<<lods[ii].mcount<<endl;
      }*/

      //todo
      if(L==lods)
         std::cout<<"no dobra\n";
      else
         std::cout<<"dupcia\n";

      fclose(fs);
      return 0;
   }
   else
   {
      //todo
      return 1;
   }
}
